#include "CMinionNode.h"

#include "CStateGame.h"
#include "COverlord.h"
#include "CBarSceneNode.h"

CMinionNode::CMinionNode(CStateGame *state, minionStats stats):CMinion(stats)
{
  this->state = state;

  delay = 0;
}

CMinionNode::~CMinionNode()
{
  if (node)
    node->drop();
}

bool CMinionNode::prepareNode(ISceneNode *node, ITexture *texture)
{
  if (node)
	{
	  this->node = node;

	  this->node->setPosition(core::vector3df(0,0,0));
	  if ( texture )
      this->node->setMaterialTexture(0, texture);

		this->node->setMaterialFlag(video::EMF_LIGHTING, false);

    // Set up the name display
    IGUIFont *font = state->getGUIEnv()->getSkin()->getFont();
    vector3df tmp(0.0f, 20.0f, 0.0f);
    SColor color = owner->getTeamColor();

    state->getSceneManager()->addTextSceneNode( font, stats.name.c_str(), color, node, tmp );


    // Set up the HP bar
    vector3df tmp2(0.0f, 50.0f, 0.0f);
    hpBar = new CBarSceneNode(this->node, state->getSceneManager(), -1, tmp2);
    hpBar->getMaterial(0).setTexture(0, state->getDriver()->getTexture("hpbar.png"));
    hpBar->drop();

		return true;
	}
	else
	{
	  // Failed to prepare the node
	  std::cout << "Fatal Error: Failed to prepare the node" << std::endl;
    return false;
	}
}


bool CMinionNode::prepareNode()
{
  ISceneManager *smgr = state->getSceneManager();

  CMatrix4<f32> trans, rot, scale, m;
  f32 x, y, z;
  IMesh* mesh;
  stringc modelFile = CMinion::getMinionModel(stats.typeName);

  if ( modelFile == "NULL" )
  {
    IMeshSceneNode *sphere = smgr->addSphereSceneNode(12, 16, state->getMinionRoot());
    m.setTranslation(vector3df(0.0f, 6.0f, 0.0f));
    smgr->getMeshManipulator()->transformMesh(sphere->getMesh(), m);
    return prepareNode(sphere , 0);
  }


  // create the XML reader
  IXMLReader *xml = (IXMLReader*) createIrrXMLReaderUTF16(modelFile.c_str());
  // file could not be opened - Default stats
  if (xml == 0)
  {
    std::cout << "Fatal Error: Could not load " << modelFile.c_str() << std::endl;
    exit(1);
  }

  // parse the file until end reached
  while(xml->read())
  {
    // Check for minion node
    stringw node(xml->getNodeName());
    if ( node == L"model" && xml->getNodeType() == EXN_ELEMENT )
    {
      stringc file = "minion\\";
      file += xml->getAttributeValue(L"file");
      mesh = smgr->getMesh(file.c_str());
    }

    if ( node == L"rotation" )
    {
      x = xml->getAttributeValueAsFloat(L"x");
      y = xml->getAttributeValueAsFloat(L"y");
      z = xml->getAttributeValueAsFloat(L"z");
      rot.setRotationDegrees(vector3df(x, y, z));
    }

    if ( node == L"translation" )
    {
      x = xml->getAttributeValueAsFloat(L"x");
      y = xml->getAttributeValueAsFloat(L"y");
      z = xml->getAttributeValueAsFloat(L"z");
      trans.setTranslation(vector3df(x, y, z));
    }

    if ( node == L"scale" )
    {
      x = xml->getAttributeValueAsFloat(L"x");
      y = xml->getAttributeValueAsFloat(L"y");
      z = xml->getAttributeValueAsFloat(L"z");
      scale.setScale(vector3df(x, y, z));
    }
  }

  // delete the xml parser after usage
  xml->drop();

  m = rot * trans;
  m = m * scale;

  smgr->getMeshManipulator()->transformMesh(mesh, m);
  smgr->getMeshManipulator()->recalculateNormals(mesh, true);

  IMeshSceneNode* node = smgr->addMeshSceneNode(mesh, state->getMinionRoot());

	#ifdef __LON_DEBUG_MODE__
  node->setDebugDataVisible(true);
  #endif

  return prepareNode( node, 0);
}


void CMinionNode::setPosition(f32 x, f32 z)
{
  //! TODO: have to deal with this later AGAIN
  f32 y = state->getTerrain()->getHeight(x, z);
  node->setPosition(vector3df(x, y, z));
}


u16 CMinionNode::attack(CMinionNode *target)
{
  // Range Check
  if ( !IsInRange(target, stats.range) )
  {
    std::cout << "Target is Out of Range. " << std::endl;
    return 0;
  }


  // Friendly Fire Check
  if ( owner == target->getOwner() && false )
  {
    std::cout << "You cannot attack your own units." << std::endl;
    return 0;
  }

  // Calculates damage
  u16 dmg = stats.atk * 4;

  // Anti overflow
  if ( dmg <= target->stats.def )
    dmg = 1;
  else
    dmg = dmg - target->stats.def;

  #ifdef __LON_DEBUG_MODE__
  dmg = 3000; // Instant kill damage
  #endif

  target->updateHp(dmg, true);

  return dmg;
}

bool CMinionNode::move(vector3df& target)
{
  // Range Check
  if ( !IsInRange(target, stats.movement) )
  {
    std::cout << "Move target is too far." << std::endl;
    return false;
  }

  moveVector = target - node->getPosition();
  moveVector.normalize();

  destination = target;

  isMoving = true;

  return true;
}

void CMinionNode::face(vector3df& target)
{
  // Facing
  vector3df pos = getNode()->getPosition();
  f32 rot = atan2(pos.Z - target.Z, - (pos.X - target.X) ) * 180 / PI;

  getNode()->setRotation(vector3df(0.0f, rot, 0.0f));
}

u16 CMinionNode::updateHp(u16 value, bool minus)
{
  // Damage
  if (minus)
  {
    if ( value > stats.hp )
      value = stats.hp;

    stats.hp -= value;

    // Display the floating DAMAGE(red) number
    state->drawFloatingText(this->getNode()->getPosition(), stringw(value), SColor(192, 255, 0, 0));
  }

  // Heal
  else
  {
    u16 result = stats.hp + value;

    if ( result > stats.maxHp )
    {
      // Sample case:
      // hp/max hp : 20/30
      // heal value: 15
      // result = 35 max hp = 30
      // heal value = 15 - ( 35 - 30 ) = 10;
      value -= ( result - stats.maxHp );
      stats.hp = stats.maxHp;
    }
    else
    {
      stats.hp += value;
    }

    // Display the floating HEALING(green) number
    state->drawFloatingText(this->getNode()->getPosition(), stringw(value), SColor(192, 0, 255, 0));
  }

  // Update the hp bar
  hpBar->setRatio(stats.hp, stats.maxHp);
  return value;
}


void CMinionNode::update()
{
  // Update movement
  if ( isMoving )
  {
    vector3df tmp = node->getPosition() + moveVector * 0.5f;
    setPosition( tmp.X, tmp.Z );

    if ( destination.getDistanceFrom( node->getPosition() ) <= 0.5f )
    {
      node->setPosition( destination );
      isMoving = false;
    }
  }
}


bool CMinionNode::IsInRange(CMinionNode *target, f32 range)
{
  vector3df sourcePos = node->getPosition();
  vector3df targetPos = target->getNode()->getPosition();

  if ( sourcePos.getDistanceFrom(targetPos) > range )
  {
    // Out of range
    return false;
  }
  else
  {
    // In range
    return true;
  }
}


bool CMinionNode::IsInRange(vector3df &targetPos, f32 range)
{
  vector3df sourcePos = node->getPosition();

  if ( sourcePos.getDistanceFrom(targetPos) > range )
  {
    // Out of range
    return false;
  }
  else
  {
    // In range
    return true;
  }
}

void CMinionNode::setDelay(u16 val)
{
  this->delay = val;
}

void CMinionNode::setOwner(COverlord* val)
{
  this->owner = val;
}

void CMinionNode::setID(u32 id)
{
  this->ID = id;
}

u16 CMinionNode::getDelay()
{
  return delay;
}

u32 CMinionNode::getID()
{
  return ID;
}

COverlord * CMinionNode::getOwner()
{
  return owner;
}


ISceneNode * CMinionNode::getNode()
{
  return node;
}
